package v1

import (
	"context"
	"fmt"
	"reflect"

	"github.com/go-logr/logr"
	corev1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/types"
	"sigs.k8s.io/controller-runtime/pkg/client"
	"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
)

// sa声明信息
type OwnServiceAccount struct {
	Name string ` json:"name"`
}

type OwnServiceAccountStatus struct {
	Name   string `json:"name"`
	Status bool   `json:"status"`
}

func (o *OwnServiceAccount) MakeOwnResource(instance *IngressNginx, logger logr.Logger,
	scheme *runtime.Scheme) (interface{}, error) {

	// new a PVC object
	sa := &corev1.ServiceAccount{
		// metadata field inherited from owner Unit
		ObjectMeta: metav1.ObjectMeta{Name: o.Name, Namespace: instance.Namespace, Labels: instance.Labels},
	}

	// add ControllerReference for sts，the owner is Unit object
	if err := controllerutil.SetControllerReference(instance, sa, scheme); err != nil {
		msg := fmt.Sprintf("set controllerReference for %s %s/%s failed", sa.Kind, instance.Namespace, sa.Name)
		logger.Error(err, msg)
		return nil, err
	}

	return sa, nil
}

// Check if the ownPVC already exists
func (o *OwnServiceAccount) OwnResourceExist(instance *IngressNginx, client client.Client,
	logger logr.Logger) (bool, interface{}, error) {

	found := &corev1.ServiceAccount{}
	err := client.Get(context.TODO(), types.NamespacedName{Name: o.Name, Namespace: instance.Namespace}, found)
	if err != nil {
		if errors.IsNotFound(err) {
			return false, nil, nil
		}

		msg := fmt.Sprintf("%s %s/%s found, but with error", found.Kind, instance.Namespace, found.Name)
		logger.Error(err, msg)
		return true, found, err
	}
	return true, found, nil
}

func (o *OwnServiceAccount) UpdateOwnResourceStatus(instance *IngressNginx, client client.Client,
	logger logr.Logger) (*IngressNginx, error) {

	found := &corev1.ServiceAccount{}
	err := client.Get(context.TODO(), types.NamespacedName{Name: o.Name, Namespace: instance.Namespace}, found)
	if err != nil {
		if errors.IsNotFound(err) {
			instance.Status.RelationResourceStatus.ServiceAccount.Status = false
			return instance, nil
		}
		return instance, err
	}

	// instance.Status.LastUpdateTime = metav1.Now()
	return instance, nil
}

// apply this own resource, create or update
func (o *OwnServiceAccount) ApplyOwnResource(instance *IngressNginx, client client.Client,
	logger logr.Logger, scheme *runtime.Scheme) error {

	// assert if PVC exist
	exist, found, err := o.OwnResourceExist(instance, client, logger)
	if err != nil {
		return err
	}

	// make PVC object
	sa, err := o.MakeOwnResource(instance, logger, scheme)
	if err != nil {
		return err
	}
	newSA := sa.(*corev1.ServiceAccount)

	// apply the PVC object just make
	if !exist {
		// if PVC not exist，then create it
		msg := fmt.Sprintf("%s %s/%s not found, create it!", newSA.Kind, newSA.Namespace, newSA.Name)
		logger.Info(msg)

		if err := client.Create(context.TODO(), newSA); err != nil {
			return err
		}

		instance.Status.RelationResourceStatus.ServiceAccount.Name = o.Name
		instance.Status.RelationResourceStatus.ServiceAccount.Status = true
		return nil
	}
	foundSA := found.(*corev1.ServiceAccount)
	// if PVC exist with change，then try to update it
	if !reflect.DeepEqual(newSA.ObjectMeta.Name, foundSA.ObjectMeta.Name) {
		msg := fmt.Sprintf("Updating ServiceAccount %s/%s", newSA.Namespace, newSA.Name)
		logger.Info(msg)
		return client.Update(context.TODO(), newSA)
	}
	return nil
}
